
local unpack = table.unpack;

local func = {};

--- 把n个对象的成员复制给obj
--- 通常用于基于原型的对象构造
--- 注意: 如果obj已经有了指定成员, 是不会被覆盖的(类似子类型的方法可以覆盖父类型)
function func.extends(obj, ...)
	local sources = {...};
	
	for i, src in ipairs(sources) do
		for name, method in pairs(src) do
            if not obj[name] then
                obj[name] = method;
            end
		end
	end
end

-------------
-- @usage printf = compose(io.write,string.format)
function func.compose(f, g) return function(...) return f(g(...)) end end

--- Compose n (right-to-left) functions into one.
function func.composeN(...)
    local fns = {...}
    local n = #fns
    return function(...)
        local state = table.pack(...)
        -- right-to-left
        for index = n, 1, -1 do state = {fns[index](unpack(state))} end
        return unpack(state)
    end
end

--[[
	对任意函数进行柯里化(转变为单参数函数)

	Example:
	function multiplyAndAdd (a, b, c) return a * b + c end

	curried_multiplyAndAdd = curry(multiplyAndAdd)

	multiplyBySevenAndAdd = curried_multiplyAndAdd(7)

	multiplySevenByEightAndAdd_v1 = multiplyBySevenAndAdd(8)
	multiplySevenByEightAndAdd_v2 = curried_multiplyAndAdd(7, 8)

	assert(multiplyAndAdd(7, 8, 9) == multiplySevenByEightAndAdd_v1(9))
	assert(multiplyAndAdd(7, 8, 9) == multiplySevenByEightAndAdd_v2(9))
	assert(multiplyAndAdd(7, 8, 9) == multiplyBySevenAndAdd(8, 9))
	assert(multiplyAndAdd(7, 8, 9) == curried_multiplyAndAdd(7, 8, 9))
]]
function func.curry(func, num_args)
    num_args = num_args or debug.info(func, "a");
    if num_args < 2 then return func end

	--- at is argument trace
    local function helper(at, n)
        if n < 1 then
            return func(unpack(flatten(at)))
        else
            return function(...)
                return helper({at, ...}, n - select("#", ...))
            end
        end
    end
    return helper({}, num_args)
end

--- 按深度优先, 把一个嵌套数组(数组元素可以是table)转成一维数组.
function flatten(t)
    local ret = {}
    for _, v in ipairs(t) do
        if type(v) == 'table' then
            for _, fv in ipairs(flatten(v)) do ret[#ret + 1] = fv end
        else
            ret[#ret + 1] = v
        end
    end
    return ret
end
func.flatten = flatten;

return func;

